package com.zhongdaima.netty.analysis.part0.jdk.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 欢迎关注公众号“种代码“，获取博主微信深入交流
 *
 * @author wangjianxin
 */
public class NioServerHandler {
    private final Selector selector;

    private final BlockingQueue<SocketChannel> prepareForRegister = new LinkedBlockingDeque<>();

    public NioServerHandler() throws IOException {
        this.selector = Selector.open();
    }

    public void register(SocketChannel socketChannel) {
        //这里为什么不直接注册呢，因为当有线程在selector上select时，register操作会阻塞
        //从未注册过channel时，start方法中的线程会一直阻塞，在这里调用register的线程也会一直阻塞
        //所以我们把待注册的channel放入队列中，并且换醒start方法中的线程，让start方法中的线程去注册

        //放入待注册队列
        try {
            this.prepareForRegister.put(socketChannel);
        } catch (InterruptedException e) {

        }
        //唤醒阻塞在selector上的线程（即下面start方法中创建的线程）
        this.selector.wakeup();

    }

    public void start() {
        Thread serverHandler = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        //只需要1个线程就可以监视所有连接
                        //当select方法返回值大于0时，说明注册到selector的Channels有我们感兴趣的事件发生
                        //返回值代表有多少Channel发生了我们感兴趣的事件
                        if (NioServerHandler.this.selector.select() > 0) {
                            //紧接着调用selectedKeys方法获取发生事件的Key集合
                            Set<SelectionKey> selectionKeys = NioServerHandler.this.selector.selectedKeys();
                            Iterator<SelectionKey> iterator = selectionKeys.iterator();
                            //遍历Key集合，处理Channel io事件
                            while (iterator.hasNext()) {
                                SelectionKey key = iterator.next();
                                try {
                                    if (key.isReadable()) {
                                        SocketChannel socketChannel = (SocketChannel) key.attachment();
                                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                                        int readLength = socketChannel.read(buffer);
                                        buffer.flip();
                                        System.out.println(new String(buffer.array(), 0, readLength));
                                        socketChannel.write(ByteBuffer.wrap(("hello, " + new String(buffer.array(), 0, readLength)).getBytes()));
                                    }
                                } finally {
                                    iterator.remove();
                                }
                            }
                        }
                        SocketChannel socketChannel;
                        while ((socketChannel = NioServerHandler.this.prepareForRegister.poll()) != null) {
                            //注册待注册的channel，感兴趣事件为OP_READ(即可读事件)
                            socketChannel.register(NioServerHandler.this.selector, SelectionKey.OP_READ, socketChannel);
                        }
                    }
                } catch (IOException e) {

                }
            }
        });
        serverHandler.start();
    }
}
